package by.epam.trenings.vkphotorest.imageLoader;

import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
import android.view.Display;
import android.view.WindowManager;
import android.widget.ImageView;
import by.epam.trenings.vkphotorest.R;
import by.epam.trenings.vkphotorest.http.HttpManager;
import by.epam.trenings.vkphotorest.utils.context.ContextHolder;

public class ImageLoader {

	public ImageLoader() {
		mLruCache = new BitmapLruCache<String>(getCacheMemory());
		mImageViews = Collections
				.synchronizedMap(new WeakHashMap<ImageView, String>());
		mExecutorService = Executors.newFixedThreadPool(MAX_THREADS);
		mException = null;
	}

	private BitmapLruCache<String> mLruCache;

	private Map<ImageView, String> mImageViews;

	private ExecutorService mExecutorService;

	private int MAX_THREADS = 3;

	private int mPlaceholder = R.drawable.ic_stub_image;

	private int mPlaceholderNoInternet = R.drawable.ic_no_internet;

	private IOException mException;

	public static final int SUCCESS_RESPONSE = 0;

	public static final int FAILURE_RESPONSE = 1;

	public static final String EXTRA_BITMAP = "downloaded bitmap";

	public static final String EXTRA_EXCEPTION = "occured exception";

	public void bind(final String url, final ImageView imageView,
			final ImageLoaderCallback callBack) {
		if (imageView == null) {
			callBack.onError(new NullPointerException("ImageView is null"));
		}
		if (url == null) {
			callBack.onError(new NullPointerException("URL is null"));
		}
		Bitmap bitmap = mLruCache.get(url);
		if (bitmap != null) {
			imageView.setImageBitmap(mLruCache.get(url));
			return;
		} else {
			mImageViews.put(imageView, url);
			imageView.setImageResource(mPlaceholder);
			BitmapToLoad bitmapToLoad = new BitmapToLoad(imageView, url);
			mExecutorService.execute(new BitmapLoader(bitmapToLoad,
					new ResultReceiver(new Handler()) {
						@Override
						protected void onReceiveResult(int resultCode,
								Bundle resultData) {
							if (resultCode == SUCCESS_RESPONSE) {
								Bitmap bitmap = resultData
										.getParcelable(EXTRA_BITMAP);
								if (!isImageViewReused(imageView, url)) {
									mImageViews.remove(imageView);
									imageView.setImageBitmap(bitmap);
								}
								if (mException != null) {
									mException = null;
								}
								callBack.onSucsess();
							} else if (resultCode == FAILURE_RESPONSE) {
								if (!isImageViewReused(imageView, url)) {
									mImageViews.remove(imageView);
									imageView
											.setImageResource(mPlaceholderNoInternet);
								}
								IOException exception = (IOException) resultData
										.getSerializable(EXTRA_EXCEPTION);
								if (mException == null) {
									mException = exception;
									callBack.onError(exception);
								} else if (!mException.getMessage().equals(
										exception.getMessage())) {
									mException = exception;
									callBack.onError(exception);
								}
							}
						}
					}));
		}
	}

	private class BitmapToLoad {

		private ImageView imageView;

		private String url;

		public BitmapToLoad(ImageView imageView, String url) {
			this.imageView = imageView;
			this.url = url;
		}

		public ImageView getImageView() {
			return imageView;
		}

		public String getUrl() {
			return url;
		}
	}

	private class BitmapLoader implements Runnable {

		private BitmapToLoad data;

		private ResultReceiver callback;

		public BitmapLoader(BitmapToLoad data, ResultReceiver callback) {
			this.data = data;
			this.callback = callback;
		}

		@Override
		public void run() {
			if (!isImageViewReused(data.getImageView(), data.getUrl())) {
				Bitmap bitmap = mLruCache.get(data.getUrl());
				if (bitmap == null) {
					try {
						bitmap = sizeProcessor(HttpManager.getInstance()
								.loadAsBitmap(data.getUrl()));
						mLruCache.put(data.getUrl(), bitmap);
					} catch (IOException e) {
						Bundle exception = new Bundle();
						exception.putSerializable(EXTRA_EXCEPTION, e);
						callback.send(FAILURE_RESPONSE, exception);
						return;
					}
				}
				Bundle bundleToSend = new Bundle();
				bundleToSend.putParcelable(EXTRA_BITMAP, bitmap);
				callback.send(SUCCESS_RESPONSE, bundleToSend);
				return;
			}
		}
	}

	/** Resize bitmap to size of displayy */
	@SuppressLint("NewApi")
	private Bitmap sizeProcessor(Bitmap bitmap) {

		int origWidth = bitmap.getWidth();
		int origHeight = bitmap.getHeight();

		WindowManager window = (WindowManager) ContextHolder.getInstance()
				.getContext().getSystemService(Context.WINDOW_SERVICE);
		Display display = window.getDefaultDisplay();
		int displayWidth;
		int displayHeight;
		if (Build.VERSION.SDK_INT > 12) {
			Point size = new Point();
			display.getSize(size);
			displayWidth = size.x;
			displayHeight = size.y;
		} else {
			displayWidth = display.getWidth();
			displayHeight = display.getHeight();
		}
		if (origWidth > displayWidth || origHeight > displayHeight) {

			while (origWidth > displayWidth || origHeight > displayHeight) {
				origWidth /= 2;
				origHeight /= 2;
			}

			bitmap = Bitmap.createScaledBitmap(bitmap, origWidth, origHeight,
					false);
		}
		return bitmap;
	}

	/**
	 * Get max capacity of LRU cache it gives 1/4 of the maximum available
	 * memory
	 */
	private int getCacheMemory() {
		int maxCachMemory;
		// imageCacheLimit measured in bits 2097152 equals 2 Mb
		int imageCacheLimit = 2097152;
		int size = (int) (Runtime.getRuntime().maxMemory() / 4);
		if (size > imageCacheLimit) {
			maxCachMemory = size;
		} else {
			maxCachMemory = imageCacheLimit;
		}
		return maxCachMemory;
	}

	/** Check if ImageView reused */
	private Boolean isImageViewReused(ImageView imageView, String url) {
		String value = mImageViews.get(imageView);
		if (value == null || !value.equals(url)) {
			return true;
		}
		return false;
	}
}